package com.cqs.example.io.serial.utils;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.BeanSerializer;

/**
 * kyro实现序列化方式
 */
public class KyroSerializer{
    private Class<?> cls = null;
    private final ThreadLocal<Output> outputLocal = new ThreadLocal<Output>();
    private final ThreadLocal<Input> inputLocal = new ThreadLocal<Input>();
    private final ThreadLocal<Kryo> kryoLocal = new ThreadLocal<Kryo>(){
        @Override
        protected Kryo initialValue() {
            Kryo kryo = new Kryo();
            kryo.register(cls, new BeanSerializer(kryo, cls));
            return kryo;
        }
    };

    public KyroSerializer(Class<?> cls) {
        this.cls = cls;
    }


    public void serialize(Object o, byte[] bytes) {
        Kryo kyro = getKyro();
        Output outPut = getOutPut(bytes);
        kyro.writeObjectOrNull(outPut, o, o.getClass());
        outPut.flush();
    }

    public void serialize(Object o, byte[] bytes, int offset, int count) {
        Kryo kyro = getKyro();
        Output outPut = getOutPut(bytes, offset, count);
        kyro.writeObjectOrNull(outPut, o, o.getClass());
        outPut.flush();
    }

    public <T> T deserialize(byte[] bytes) {
        return deserialize(bytes,0,bytes.length);
    }

    public <T> T deserialize(byte[] bytes, int offset, int count) {
        Kryo kyro = getKyro();
        Input input = getInput(bytes, offset, count);
        return (T) kyro.readObjectOrNull(input, cls);
    }

    private Input getInput(byte[] bytes, int offset, int count) {
        Input input = null;
        if ((input = inputLocal.get()) == null) {
            input = new Input();
            inputLocal.set(input);
        }
        if (bytes != null) {
            input.setBuffer(bytes, offset, count);
        }
        return input;
    }

    private Output getOutPut(byte[] bytes) {
        Output output = null;
        if ((output = outputLocal.get()) == null) {
            output = new Output();
            outputLocal.set(output);
        }
        if (bytes != null) {
            output.setBuffer(bytes);
        }
        return output;
    }
    private Output getOutPut(byte[] bytes, int offset, int count) {
        Output output = null;
        if ((output = outputLocal.get()) == null) {
            output = new Output();
            outputLocal.set(output);
        }
        if (bytes != null) {
            output.writeBytes(bytes, offset, count);
        }
        return output;
    }

    private Kryo getKyro(){
        return kryoLocal.get();
    }
    public Class<?> getCls() {
        return cls;
    }

    public void setCls(Class<?> cls) {
        this.cls = cls;
    }
}
